#pragma once

#include "ly/export.hpp"
#include "ly/pcell_declaration.hpp"

#include <map>
#include <memory>
#include <string>

namespace ly {

class PCellVariant;
class PCellDeclaration;

struct LY_API PCellParametersCompareFunc{
    // bool operator()(const pcell_parameters_type* a, const pcell_parameters_type* b) const;
};

/**
 *  @brief A PCell header
 *
 *  The PCell header manages the PCell variants along with the PCell declaration.
 */
class LY_API PCellHeader {
 public:
  typedef std::shared_ptr<PCellHeader> sptr;
  typedef std::vector<ly::Variant> pcell_parameters_type;

  // typedef std::map<pcell_parameters_type*, ly::PCellVariant*, PCellParametersCompareFunc> variant_map_t;
  typedef std::map<pcell_parameters_type*, ly::PCellVariant*> variant_map_t;
  typedef variant_map_t::const_iterator variant_iterator;

  /**
   *  @brief The default constructor
   */
  PCellHeader(size_t pcell_id, const std::string& name, PCellDeclaration* declaration);

  /**
   *  @brief The destructor
   */
  ~PCellHeader();

  /**
   *  @brief Copy constructor
   */
  PCellHeader(const PCellHeader& d);

  /**
   *  @brief Get the name
   */
  const std::string& get_name() const { return m_name; }

  /**
   *  @brief Get the declaration
   */
  const PCellDeclaration* declaration() const { return mp_declaration; }

  /**
   *  @brief Set the declaration
   *
   *  The declaration can be 0, in which case the PCell cannot produce any code.
   */
  void declaration(PCellDeclaration* declaration);

  /**
   *  @brief Get the layer index list for this PCell declaration for the given parameter set
   */
  // std::vector<unsigned int> get_layer_indices(ly::Layout& layout, const pcell_parameters_type& parameters, ly::ImportLayerMapping* layer_mapping = 0);

  /**
   *  @brief Get the variant for a given parameter set if it already exists.
   *
   *  If no variant is registered for this parameter set, 0 is returned.
   */
  PCellVariant* get_variant(ly::Layout& layout, const pcell_parameters_type& parameters);

  /**
   *  @brief Register a variant 
   */
  void unregister_variant(PCellVariant* variant);

  /**
   *  @brief Unregister a variant 
   */
  void register_variant(PCellVariant* variant);

  /**
   *  @brief Iterates the variants (begin)
   */
  variant_iterator begin() const { return m_variant_map.begin(); }

  /**
   *  @brief Iterates the variants (end)
   */
  variant_iterator end() const { return m_variant_map.end(); }

  /**
   *  @brief Get the PCell Id for this variant
   */
  size_t pcell_id() const { return m_pcell_id; }

 private:
  variant_map_t m_variant_map;
  ly::PCellDeclaration* mp_declaration;
  size_t m_pcell_id;
  std::string m_name;
};

}  // namespace ly
